package qq2564874169.tuaotuao.fx.orm.mybatis;


import org.apache.ibatis.builder.StaticSqlSource;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import qq2564874169.tuaotuao.fx.FxAutoCloseable;

import java.util.*;

@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class PageInterceptor implements Interceptor {
    private static ThreadLocal<SqlPager> locPager = new ThreadLocal<>();

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if (locPager.get() == null) {
            return invocation.proceed();
        }
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object po = args[1];
        BoundSql boundSql = ms.getBoundSql(po);
        List<ParameterMapping> pm = new ArrayList<>();
        List<ParameterMapping> spm = boundSql.getParameterMappings();
        if (spm.size() > 0) {
            pm.addAll(spm);
            pm.addAll(spm);
            if (po != null) {
                Map<String, Object> newPo = new HashMap<>(spm.size());
                MetaObject metaObject = ms.getConfiguration().newMetaObject(po);
                for (ParameterMapping item : spm) {
                    if (item.getMode() == ParameterMode.OUT) {
                        throw new RuntimeException("不支持OUT参数");
                    }
                    String name = item.getProperty();
                    if (name.contains(".")) {
                        name = name.substring(0, name.indexOf("."));
                    }
                    if (newPo.containsKey(name)) {
                        continue;
                    }
                    if (boundSql.hasAdditionalParameter(name)) {
                        newPo.put(name, boundSql.getAdditionalParameter(name));
                    } else if (metaObject.hasGetter(name)) {
                        newPo.put(name, metaObject.getValue(name));
                    }
                }
                args[1] = newPo;
            }
        }
        SqlPager pager = locPager.get();
        String csql = pager.countSql(boundSql.getSql());
        String esql = pager.dataSql(boundSql.getSql());
        String sql = csql + ";" + esql;
        SqlSource newSqlSource = new StaticSqlSource(ms.getConfiguration(), sql, pm);
        MappedStatement pageMs = copyFromMappedStatement(ms, ms.getId() + "ByPage", newSqlSource);
        args[0] = pageMs;
        return invocation.proceed();
    }

    private MappedStatement copyFromMappedStatement(MappedStatement ms, String msid, SqlSource newSqlSource) {
        MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), msid, newSqlSource, ms.getSqlCommandType());
        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        builder.keyGenerator(ms.getKeyGenerator());
        if (ms.getKeyProperties() != null) {
            builder.keyProperty(String.join(",", ms.getKeyProperties()));
        }
        if (ms.getKeyColumns() != null) {
            builder.keyColumn(String.join(",", ms.getKeyColumns()));
        }
        builder.cache(ms.getCache());
        builder.databaseId(ms.getDatabaseId());
        builder.flushCacheRequired(ms.isFlushCacheRequired());
        builder.lang(ms.getLang());
        builder.useCache(ms.isUseCache());
        builder.resultSetType(ms.getResultSetType());
        List<ResultMap> maps = new ArrayList<>();
        ResultMap.Builder rmBuilder = new ResultMap.Builder(ms.getConfiguration(), builder.id(), Integer.class, new ArrayList<>());
        maps.add(rmBuilder.build());
        maps.addAll(ms.getResultMaps());
        builder.resultMaps(maps);
        return builder.build();
    }

    @Override
    public Object plugin(Object o) {
        return Plugin.wrap(o, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    public FxAutoCloseable pageSpace(SqlPager pager) {
        locPager.set(pager);
        return () -> locPager.remove();
    }
}
